package top.codetheory.thread._008_CAS;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
import lombok.extern.slf4j.Slf4j;

/**
 * @author:
 * @since: 2024/5/30 上午10:34
 **/
@Slf4j
public class _001_String的ABA问题 {
    static AtomicReference<Cat> reference = new AtomicReference<>(new Cat("小黑"));
    static Cat prev = reference.get();

    public static void main(String[] args) {
        test2();
    }

    /**
     * 可以看到，main方法无法检测到变量已经被修改过 <br>
     * 他只能判断这个值的地址是跟原来的地址一样的，但是无法判断他是不是被修改过 <br>
     *<img src="https://gitee.com/codetheory/img-on1/raw/master/img01/1717040144306-2024-5-3011:35:45.png"  />
     */
    public static void test1() {

        other();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("change 小黑->小白 {}",reference.compareAndSet(prev, new Cat("小白")));
    }

    public static void other() {
        new Thread(() -> {log.info("change 小黑->小红 {}",reference.compareAndSet(reference.get(), new Cat("小红")));}, "t1").start();
        new Thread(() -> {log.info("change 小红->小黑 {}", reference.compareAndSet(reference.get(), prev));}, "t2").start();
    }

    static AtomicStampedReference<Cat> referenceVersion = new AtomicStampedReference<>(new Cat("小黑"), 0);

    /**
     * 使用带版本号的原子引用，可以解决这个问题 <br>
     * <img src="https://gitee.com/codetheory/img-on1/raw/master/img01/1717041148293-2024-5-3011:52:29.png"  />
     */
    public static void test2() {
        Cat prev = referenceVersion.getReference();
        int stamp = referenceVersion.getStamp();
        otherVersion();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("当前版本{}，change 小黑->小白 {}",stamp,referenceVersion.compareAndSet(prev, new Cat("小白"), stamp, stamp + 1));
    }

    private static void otherVersion() {
        new Thread(() -> {
            int stamp = referenceVersion.getStamp();
            log.info("当前版本{}，change 小黑->小红 {}",stamp,referenceVersion.compareAndSet(
                referenceVersion.getReference(),
                new Cat("小红"),
                stamp,
                stamp + 1));
            }, "t1").start();

        new Thread(() -> {
            int stamp = referenceVersion.getStamp();
            log.info("当前版本{}，change 小红->小黑 {}",stamp, referenceVersion.compareAndSet(
                referenceVersion.getReference(),
                prev,
                stamp,
                stamp + 1));
            }, "t2").start();
    }

    static class Cat {
        String name;

        public Cat(String name) {
            this.name = name;
        }

        @Override
        public boolean equals(Object object) {
            Cat cat = (Cat) object;
            return Objects.equals(name, cat.name);
        }

        @Override
        public int hashCode() {
            return 1;
        }
    }

}